﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Registry;
using SdmxMl.Manager;
namespace SdmxMl.Structure
{

    /// <summary>
    /// Helper class, ensures that only text are retrived from proposed artefact update.
    /// This allows to update a finalized artefact without risk of item change.
    /// </summary>
    public class ArtefactTextUpdater
    {
        #region Internal members

        /// <summary>Finalized artefact where texts are to be updated</summary>
        private Artefact arFinalized;

        /// <summary>similar artefact with texts updated</summary>
        private Artefact arNewText;

        /// <summary>Unique ID of finalized artefact</summary>
        private string fullId;

        #endregion

        public bool AnnotationOnly { get; set; }

        #region C'tor

        /// <summary> Constructor </summary>
        /// <param name="xDocFinal">xml document containing finalized artefact</param>
        /// <param name="xDocUpdatedText">xml document containing updated text artefact</param>
        /// <param name="fid">Artefact full ID</param>
        public ArtefactTextUpdater(XmlDocument xDocFinal, XmlDocument xDocUpdatedText, string fid)
        {
            fullId = fid.ToLower();
            arFinalized = GetArtefactFromDoc(xDocFinal);
            arNewText = GetArtefactFromDoc(xDocUpdatedText);

            if (arNewText == null)
                throw new ApplicationException("Unable to fetch artefact with updated texts");
            if (arFinalized == null)
                throw new ApplicationException("Unable to fetch finalized artefact");
        }
        #endregion

        #region Helpers

        private Artefact GetArtefactFromDoc(XmlDocument xDoc)
        {
            Artefact ar = null;
            SmManager sm = new SmManager();
            sm.LoadSdmxDocument(null, true, xDoc);

            foreach (ArtefactManager am in sm.ArtefactManagerDico.Values)
                foreach (Artefact a in am.ArtefactList)
                    if (a.FullIdent.ToLower() == fullId)
                    {
                        ar = a;
                        break;
                    }

            return ar;
        }
        #endregion

        #region Public Methods

        public XmlDocument Update()
        {
            XmlDocument xDoc = null;

            if (arFinalized.IsFinal)
            {
                ArtefactUpdate();
                SmManager sm = new SmManager();
                sm.PutArtefact(arFinalized.ArType, arFinalized);
                
                if (arFinalized.ArType == SdmxArtefactType.MetadataSet)
                    xDoc = sm.GetMetasetAsDocument(arFinalized.FullIdent);
                else
                    xDoc = sm.GetAsDocument();
            }

            return xDoc;
        }
        #endregion

        #region Text Update

        private void ArtefactUpdate()
        {
            // The artefact itself
            arFinalized.Name = arNewText.Name.Clone();
            arFinalized.Description = arNewText.Description.Clone();
            arFinalized.ValidTo = arNewText.ValidTo;

            // some artefact items
            if (arFinalized.ArType == SdmxArtefactType.CodeLists)
            {
                TextCodeUpdate((arNewText as CodeListType).CodeList, (arFinalized as CodeListType).CodeList);
            }
            else if (arFinalized.ArType == SdmxArtefactType.Concepts)
            {
                TextConceptUpdate((arNewText as ConceptSchemeType).ConceptList, (arFinalized as ConceptSchemeType).ConceptList);
            }
            else if (arFinalized.ArType == SdmxArtefactType.CategorySchemes)
            {
                TextCategoryUpdate(arNewText as CategorySchemeType, arFinalized as CategorySchemeType);
            }
            else if (arFinalized.ArType == SdmxArtefactType.HierarchicalCodelists)
            {
                TextHierarchyUpdate(arNewText as HierarchicalCodelistType, arFinalized as HierarchicalCodelistType);
            }

        }

        private void TextCategoryUpdate(CategorySchemeType catSource, CategorySchemeType catDest)
        {
            CategoryListUpdate(catDest.CategoryList, catSource);
        }

        private void CategoryListUpdate(CategoryList catList, CategorySchemeType catSource)
        {
            foreach (Category item in catList)
            {
                Category itemWithtext = catSource.CategoryList.LocateItemFromFullId(item.FullId, catSource.CategoryList);
                if (itemWithtext != null)
                {
                    if (AnnotationOnly == false)
                    {
                        item.Name = itemWithtext.Name.Clone();
                        item.Description = itemWithtext.Description.Clone();
                    }
                    AnnotationUpdate(itemWithtext.AnnotationList, item.AnnotationList);

                    CategoryListUpdate(itemWithtext.CategoryList, catSource);
                }
            }
        }

        private void TextCodeUpdate(CodeList listSource, CodeList listDest)
        {
            foreach (CodeType item in listDest)
            {
                CodeType itemWithtext = listSource.LocateItem(item.Id) as CodeType;
                if (itemWithtext != null)
                {
                    if (AnnotationOnly == false)
                    {
                        item.Name = itemWithtext.Name.Clone();
                        item.Description = itemWithtext.Description.Clone();
                    }
                    AnnotationUpdate(itemWithtext.AnnotationList, item.AnnotationList);
                }
            }
        }

        private void TextHierarchyUpdate(HierarchicalCodelistType arNewText, HierarchicalCodelistType arFinalized)
        {
            foreach (HierarchyType hi in arFinalized.HierarchyList)
                foreach (HierarchyType hiNew in arNewText.HierarchyList)
                    if (hi.Id == hiNew.Id)
                    {
                        hi.Name = hiNew.Name.Clone();
                        hi.Description = hiNew.Description.Clone();
                    }
                
        }

        private void TextConceptUpdate(ConceptList listSource, ConceptList listDest)
        {
            foreach (ConceptType item in listDest)
            {
                ConceptType itemWithtext = listSource.LocateItem(item.Id) as ConceptType;
                if (itemWithtext != null)
                {
                    if (AnnotationOnly == false)
                    {
                        item.Name = itemWithtext.Name.Clone();
                        item.Description = itemWithtext.Description.Clone();
                    }
                    AnnotationUpdate(itemWithtext.AnnotationList, item.AnnotationList);
                }
            }
        }

        private void AnnotationUpdate(AnnotationList listSource, AnnotationList listDest)
        {
            /* NOT USED but who knows
             * 
            // If new source has an annotation
            if (listSource.Count > 0)
            {
                // get it check if dest has already same annotation
                Annotation a = listSource[0]; // has only one

                Annotation aDest = listDest.GetAnnotationType(a.Type);

                // if yes, update text content, if no add annotation
                if (aDest != null)
                    aDest.Text = a.Text.Clone();
                else
                    listDest.Add(a);
            }
             */
        }


        #endregion
    }
}
